﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Design;
using System.IO;
using System.Linq;
using System.Runtime;
using System.Runtime.InteropServices;
using System.Text;
using System.Windows.Forms;

using Storm;
using Storm.Docking;
using Storm.Docking.Controls;
using Storm.Docking.Controls.Float;
using Storm.Docking.Visual;
using Storm.Docking.Visual.Drawing;
using Storm.Docking.Visual.Glyphs;

namespace Storm.Docking.Visual
{
	#region TabMouseEventArgs

	/// <summary>
	/// Class containing information about a Tab mouse event.
	/// </summary>
	public class TabMouseEventArgs
		: EventArgs
	{
		#region Fields

		private TabPage tabPage = null;

		#endregion

		#region Properties

		/// <summary>
		/// Gets or sets the TabPage associated with the TabMouseEventArgs.
		/// </summary>
		[Browsable(false)]
		[Description("Gets or sets the TabPage associated with the TabMouseEventArgs.")]
		public TabPage TabPage
		{
			get { return tabPage; }
			set { tabPage = value; }
		}

		#endregion

		/// <summary>
		/// Initializes a new instance of TabMouseEventArgs.
		/// </summary>
		public TabMouseEventArgs()
		{
		}

		/// <summary>
		/// Initializes a new instance of TabMouseEventArgs.
		/// </summary>
		/// <param name="page">TabPage associated with the TabMouseEventArgs.</param>
		public TabMouseEventArgs(TabPage page)
		{
			this.TabPage = page;
		}
	}

	#endregion

	/// <summary>
    /// Represents a TabControl in which the visible Form can be 
    /// changed by changing the SelectedTab.
    /// </summary>
	[ToolboxItem(false)]
    public class DockTab
        : System.Windows.Forms.TabControl
    {
        #region Fields

        private int          hoverIndex   = -1;
        private StringFormat stringFormat = null;

		private DockRenderer renderer  = null;
		private DockPanel    dockPanel = null;
		private DockPane     dockPane  = null;

		private Point lastMousePos = new Point(0, 0);

		#region Events

		/// <summary>
		/// Occurs when the user's mouse enters a Tab.
		/// </summary>
		public event TabMouseEventHandler TabMouseEnter;

		/// <summary>
		/// Occurs when the user's mouse leaves a Tab.
		/// </summary>
		public event TabMouseEventHandler TabMouseLeave;

		#endregion

		#region Delegates

		/// <summary>
		/// Represents the method that will handle the TabMouseEnter 
		/// event or the TabMouseLeave event.
		/// </summary>
		/// <param name="sender">Sender object.</param>
		/// <param name="e">TabMouseEventArgs.</param>
		public delegate void TabMouseEventHandler(object sender, TabMouseEventArgs e);

		#endregion

		#endregion

		#region Properties

		/// <summary>
		/// Gets or sets the DockRenderer of the DockTab.
		/// </summary>
		[Browsable(false)]
		[Description("Gets or sets the DockRenderer of the DockTab.")]
		public DockRenderer Renderer
		{
			get { return renderer; }
			set
			{
				renderer = value;

				// Loop through all the TabPages and their Controls
				// to update them and their Renderers.

				foreach (TabPage page in this.TabPages)
				{
					foreach (Control control in page.Controls)
					{
						if (control is DockContainer)
							(control as DockContainer).Renderer = this.Renderer;
					}
				}

				this.Invalidate();
			}
		}

		/// <summary>
        /// Gets the index of the tab the user is currently hovering 
        /// over with the mouse.
        /// </summary>
		[Browsable(false)]
		[Description("Gets the index of the tab the user is currently hovering over with the mouse.")]
        public int HoverIndex
        {
            get { return hoverIndex; }
        }

		/// <summary>
		/// Gets the StringFormat used to draw the TabPages' text.
		/// </summary>
		[Browsable(false)]
		[Description("Gets the StringFormat used to draw the TabPages' text.")]
		public StringFormat StringFormat
		{
			get { return stringFormat; }
		}

		/// <summary>
		/// Gets or sets the parent DockPanel.
		/// </summary>
		[Browsable(true)]
		[Description("Gets or sets the parent DockPanel.")]
		public DockPanel DockPanel
		{
			get { return dockPanel; }
			set
			{
				dockPanel = value;

				// Update the DockPanel/DockPane fields of the
				// DockCaption to make sure the DockCaption is
				// where it should be.

				dockPanel.Controls.Add(this);
				this.Parent = dockPanel;

				if (dockPanel.DockPane != null)
					dockPane = dockPanel.DockPane;
			}
		}

		/// <summary>
		/// Gets the parent DockPane.
		/// </summary>
		[Browsable(false)]
		[Description("Gets the parent DockPane.")]
		public DockPane DockPane
		{
			get { return dockPane; }
		}

        #endregion

        #region Methods

        #region Internal

		/// <summary>
		/// Calls InvokePaint and InvokePaintBackground.
		/// </summary>
		/// <param name="c">Control for the invokes.</param>
		/// <param name="e">PaintEventArgs for the invokes.</param>
		internal void InvokePaintAll(Control c, PaintEventArgs e)
		{
			this.InvokePaint(c, e);
			this.InvokePaintBackground(c, e);
		}

        #endregion

		#region Protected

		/// <summary>
		/// Raises the <see cref="E:System.Windows.Forms.Control.Paint"/> event.
		/// </summary>
		/// <param name="e">A <see cref="T:System.Windows.Forms.PaintEventArgs"/> that contains the event data.</param>
		protected override void OnPaint(PaintEventArgs e)
		{
			this.Renderer.TabPaintBackground(this, e.Graphics);
			for (int i = 0; i < this.TabCount; i++)
			{
				this.Renderer.TabDrawTab(this, e.Graphics,
					this.TabPages[i], i);
			}

			base.OnPaint(e);
		}

		/// <summary>
		/// Paints the background of the control.
		/// </summary>
		/// <param name="pevent">A <see cref="T:System.Windows.Forms.PaintEventArgs"/> that contains information about the control to paint.</param>
		protected override void OnPaintBackground(PaintEventArgs pevent)
		{
			this.Renderer.TabPaintBackground(this,
				pevent.Graphics);
		}

		/// <summary>
		/// Raises the <see cref="E:System.Windows.Forms.Control.MouseMove"/> event.
		/// </summary>
		/// <param name="e">A <see cref="T:System.Windows.Forms.MouseEventArgs"/> that contains the event data.</param>
		protected override void OnMouseMove(MouseEventArgs e)
		{
			base.OnMouseMove(e);

			bool foundItem = false;
			int index = -1;

			foreach (TabPage page in this.TabPages)
			{
				index++;
				if (this.GetTabRect(index).Contains
					(e.Location) == true)
				{
					hoverIndex = index;
					this.Invalidate();
					foundItem = true;

					if (this.TabMouseEnter != null)
					{
						TabMouseEventArgs tabArgs = new TabMouseEventArgs();
						tabArgs.TabPage = page;

						this.TabMouseEnter(this, tabArgs);
					}
				}
				else if (foundItem == false)
				{
					hoverIndex = -1;
					this.Invalidate();

					if (this.TabMouseLeave != null)
					{
						TabMouseEventArgs tabArgs = new TabMouseEventArgs();
						tabArgs.TabPage = page;

						this.TabMouseLeave(this, tabArgs);
					}
				}
			}

			// We use Cursor.Position != lastMousePos to check whether the mouse is actually
			// moving. (http://blogs.msdn.com/oldnewthing/archive/2003/10/01/55108.aspx)

			if (e.Button == MouseButtons.Left && Cursor.Position != lastMousePos)
			{
				if (hoverIndex != -1)
				{
					// Store the current size so we can resize the DockPanel later. (BUG FIX)

					Size beforeSize = this.Size;

					// Store the current DockStyle to a variable so we can re-use it when we 
					// will re-add all the remaining Forms to the parent DockPane.

					DockStyle dockStyle = this.DockPanel.Dock;

					// Aaaand hide the dock Rectangles.

					this.DockPanel.DockPane.HideDockRectangles();

					this.DockPanel.MakeFloating();
					this.DockPanel.DockPanelState = DockPanelState.Docked;

					DockForm dockForm = this.DockPanel.DockForm;
					dockForm.Renderer = this.DockPanel.Renderer;

					this.DockPanel.DockPane.UpdateIcons();

					// Loop through all the TabPages of the DockTab and remove all TabPages that
					// are not the MainTabPage of the parent DockPanel.

					List<TabPage> removePages = new List<TabPage>();
					foreach (TabPage page in this.TabPages)
					{
						if (hoverIndex == -1)
						{
							if (page != this.TabPages[this.SelectedIndex])
								removePages.Add(page);
						}
						else
						{
							if (page != this.TabPages[hoverIndex])
								removePages.Add(page);
						}
					}

					// We found all the tabs that needs to be removed - remove them from this
					// DockTab, and re-add their contents to the DockPane.

					foreach (TabPage page in removePages)
					{
						foreach (Control control in page.Controls)
						{
							if (control is DockContainer)
							{
								DockContainer container = control as DockContainer;

								// Add the found Form to the parent DockPane.
								DockPanel newDockPanel = this.DockPanel.DockPane.CreatePanel(container.Form, dockStyle);
								newDockPanel.Size = beforeSize;

								// Finally remove the TabPage.
								this.TabPages.Remove(page);
							}
						}
					}
				}
			}

			lastMousePos = Cursor.Position;
		}

		/// <summary>
		/// Raises the <see cref="E:System.Windows.Forms.Control.MouseLeave"/> event.
		/// </summary>
		/// <param name="e">An <see cref="T:System.EventArgs"/> that contains the event data.</param>
		protected override void OnMouseLeave(EventArgs e)
		{
			hoverIndex = -1; 	// Reset HoverIndex.
			this.Invalidate();

			if (this.TabMouseLeave != null)
			{
				TabMouseEventArgs tabArgs = new TabMouseEventArgs();
				tabArgs.TabPage = null;

				this.TabMouseLeave(this, tabArgs);
			}

			base.OnMouseLeave(e);
		}

		/// <summary>
		/// Raises the <see cref="E:System.Windows.Forms.TabControl.SelectedIndexChanged"/> event.
		/// </summary>
		/// <param name="e">An <see cref="T:System.EventArgs"/> that contains the event data.</param>
        protected override void OnSelectedIndexChanged(EventArgs e)
        {
            if (this.TabPages.Count == 0 && 
                this.FindForm() is DockForm)
            {
                this.FindForm().Hide();
            }

            this.UpdateTabSelection(true);  // Update.
        }

		#endregion

		#endregion

		/// <summary>
		/// Initializes a new instance of the <see cref="DockTab"/> class.
		/// </summary>
        public DockTab()
        {
			renderer = new DockRenderer();

            // Call SetStyle multiple times to prevent flickering.
            
            this.SetStyle(ControlStyles.UserPaint, true);
            this.SetStyle(ControlStyles.OptimizedDoubleBuffer, true);
            this.SetStyle(ControlStyles.ResizeRedraw, true);
            this.SetStyle(ControlStyles.SupportsTransparentBackColor, true);
            
			// Setup the settings of the base TabControl.
			this.Dock = DockStyle.Fill;
			this.BackColor = this.Renderer.DockColorTable.TabBodyBackColor;
			this.Font = this.Renderer.DockColorTable.PaneFont;
			
            this.Alignment = TabAlignment.Bottom;
            this.SizeMode = TabSizeMode.Fixed;
            this.ShowToolTips = true;

            // Initialize the StringFormat that we use for drawing
            // text on the tabs.

            stringFormat = new StringFormat();
            stringFormat.Alignment = StringAlignment.Center;
            stringFormat.LineAlignment = StringAlignment.Center;
        }
    }
}
